package com.xxrl.shop.config;

import com.xxrl.shop.config.handler.*;
import com.xxrl.shop.config.service.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/**
 * @author mis
 * @implNote https://blog.csdn.net/I_am_Hutengfei/article/details/100561564
 * most from the blog
 */
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    /**
     * 登录失败处理逻辑
     */
    private final CustomizeAuthenticationFailureHandler authenticationFailureHandler;

    /**
     * 安全元数据源
     * 根据路径来返回权限
     */
    private final CustomizeFilterInvocationSecurityMetadataSource securityMetadataSource;

    /**
     * 登录成功处理逻辑
     */
    private final CustomizeAuthenticationSuccessHandler authenticationSuccessHandler;

    /**
     * 权限拒绝处理逻辑
     */
    private final CustomizeAccessDeniedHandler accessDeniedHandler;

    /**
     * 匿名用户访问无权限资源时的异常
     */
    private final CustomizeAuthenticationEntryPoint authenticationEntryPoint;

    /**
     * 会话失效(账号被挤下线)处理逻辑
     */
    private final CustomizeSessionInformationExpiredStrategy sessionInformationExpiredStrategy;

    /**
     * 登出成功处理逻辑
     */
    private final CustomizeLogoutSuccessHandler logoutSuccessHandler;

    /**
     * 访问决策管理器
     */
    private final CustomizeAccessDecisionManager accessDecisionManager;

    private final UserDetailsServiceImpl userDetailsService;

    public SecurityConfiguration(CustomizeAuthenticationFailureHandler authenticationFailureHandler,
                                 CustomizeAuthenticationSuccessHandler authenticationSuccessHandler,
                                 CustomizeAccessDeniedHandler accessDeniedHandler,
                                 CustomizeAuthenticationEntryPoint authenticationEntryPoint,
                                 CustomizeSessionInformationExpiredStrategy sessionInformationExpiredStrategy,
                                 CustomizeLogoutSuccessHandler logoutSuccessHandler,
                                 CustomizeAccessDecisionManager accessDecisionManager,
                                 CustomizeFilterInvocationSecurityMetadataSource securityMetadataSource,
                                 UserDetailsServiceImpl userDetailsService) {
        this.authenticationFailureHandler = authenticationFailureHandler;
        this.authenticationSuccessHandler = authenticationSuccessHandler;
        this.accessDeniedHandler = accessDeniedHandler;
        this.authenticationEntryPoint = authenticationEntryPoint;
        this.sessionInformationExpiredStrategy = sessionInformationExpiredStrategy;
        this.logoutSuccessHandler = logoutSuccessHandler;
        this.accessDecisionManager = accessDecisionManager;
        this.securityMetadataSource = securityMetadataSource;
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        //获取用户账号密码及权限信息
        return userDetailsService;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setAccessDecisionManager(accessDecisionManager);//决策管理器
                        o.setSecurityMetadataSource(securityMetadataSource); //安全元数据源
                        return o;
                    }
                })
                //登出
                .and().logout().permitAll()
                //登出成功处理逻辑
                //登出之后删除cookie
                .logoutSuccessHandler(logoutSuccessHandler)
                .deleteCookies("JSESSIONID")

                //登入
                .and().formLogin().permitAll()
                //登录失败处理逻辑
                .successHandler(authenticationSuccessHandler)
                //登录失败处理逻辑
                .failureHandler(authenticationFailureHandler)

                //异常处理(权限拒绝、登录失效等)
                .and().exceptionHandling()
                //权限拒绝处理逻辑
                .accessDeniedHandler(accessDeniedHandler)
                //匿名用户访问无权限资源时的异常处理
                .authenticationEntryPoint(authenticationEntryPoint)

                //会话管理
                .and().sessionManagement()
                //同一账号同时登录最大用户数
                .maximumSessions(1)
                //会话失效(账号被挤下线)处理逻辑
                .expiredSessionStrategy(sessionInformationExpiredStrategy);
    }


}
